import { getClient, isAbsoluteURL, transformQueryString, transformURL } from "./utils";
import $config from "../config";
import { Response } from "../type";
import CryptoJS from "crypto-js";

const { appName, osName, deviceBrand, deviceModel, deviceId, appVersionCode, appWgtVersion, appVersion, uniPlatform } = uni.getSystemInfoSync();

export const encryptData = (data = {}) => {
  const {
    MD5,
    AES: { encrypt },
    mode: { CBC },
    pad: { Pkcs7 },
    enc: { Utf8 } } = CryptoJS;

  const originData = JSON.parse(JSON.stringify(data));
  Object.assign(originData, {
    uid: uni.getStorageSync($config.STORAGE_KEY_USER_INFO)?.value?.id || "",
    app_version: appWgtVersion || appVersion,
    app_view: osName,
    device_id: deviceId,
    device_name: deviceModel,
    timestamp: Date.now().toString().slice(0, 10),
  });
  const newData: AnyObject = {};
  Object.keys(originData).sort().forEach(key => (newData[key] = originData[key]));
  for (let key in newData) newData[key] ?? delete newData[key];
  newData.sign = MD5(transformQueryString(newData) + "&key=97003ee0a511884cfa7b2b561e0cd112").toString().toLocaleUpperCase();

  return {
    sign_str: encrypt(JSON.stringify(newData),
      Utf8.parse('forceGFpT66AjkTM'), {
      iv: Utf8.parse('force2022501luck'),
      mode: CBC,
      padding: Pkcs7
    }).toString()
  };
}

export function requestInvoke(
  args: UniApp.RequestOptions & UniApp.UploadFileOption,
  paramsKey: "data" | "formData"
): UniApp.RequestOptions | UniApp.UploadFileOption {

  if (!isAbsoluteURL(args.url)) {
    let prefix = "@encrypt";
    if (/^@/.test(args.url)) {
      const url = args.url.split("/");
      prefix = url[0];
      args.url = "/" + url.slice(1).join("/");
    }

    args[paramsKey] ?? (args[paramsKey] = {});
    args.header ?? (args.header = {});
    paramsKey === "data" && (args.header["Content-type"] = "application/json");
    args.header["Cache-Control"] = "no-cache";

    if (!(new RegExp(`(^${$config.ASSETS_PREFIX}/static)/`).test(args.url))) {
      const token = uni.getStorageSync($config.STORAGE_KEY_USER_INFO)?.value?.[$config.FIELD_TOKEN];
      token && (args.header[$config.FIELD_TOKEN] = token);
      args.url = ($config.URL_REMOTE || "") + ($config.API_PREFIX || "") + args.url;

      switch (prefix) {
        case "@encrypt":
          args.method = "POST";
          args[paramsKey] = encryptData(args[paramsKey]);
          break;
        default:
          args.header["lang"] = uni.getLocale();
          args.header["version"] = appWgtVersion || appVersion;
          args.header["client"] = getClient();
          break;
      }
    }
  }

  for (let key in args[paramsKey]) { args[paramsKey][key] ?? delete args[paramsKey][key]; }
  return args;
}

let debounce = false;
export function requestSuccess({ data, statusCode }: UniApp.RequestSuccessCallbackResult) {
  try {
    const res = (typeof data === "string") ? JSON.parse(data) : data;
    const { code } = res,
      codeRes = (code || code === 0) ? Number(code) : null;

    switch (codeRes) {
      case null:
        return Promise.resolve({ msg: "非标准响应格式需手动处理", data: res.data, code: -9 });
      case 400:
      case 500:
      case 217:
      case 405:
        uni.showToast({ title: res.msg, icon: "none" });
        return Promise.reject(res);
      case 706:
        uni.showModal({
          title: "温馨提示",
          content: res.msg,
          showCancel: false,
          success() {
            // #ifdef APP-PLUS
            plus.runtime.restart();
            // #endif
            // #ifndef APP-PLUS
            uni.reLaunch({ url: $config.PAGE_HOME_CLIENT });
            // #endif
          }
        });
        return Promise.reject(res);
      case 401:
      case 402:
        if (debounce) return Promise.reject(res.msg);
        debounce = true;
        uni.hideToast();
        uni.showModal({
          title: "温馨提示",
          content: res.msg,
          showCancel: false,
          success({ confirm }) {
            uni.removeStorage({ key: $config.STORAGE_KEY_USER_INFO });
            uni.reLaunch({
              url: confirm ? [
                // #ifdef MP
                $config.PAGE_LOGIN_APPLETS,
                // #endif
                // #ifndef MP
                $config.PAGE_LOGIN_ACCOUNT
                // #endif
              ][0] || "" : $config.PAGE_HOME_CLIENT
            });
          },
          complete: () => {
            debounce = false;
          }
        });
        return Promise.reject(res.msg);
      case 218:
        uni.hideToast();
        uni.showModal({
          title: "温馨提示",
          content: res.msg,
          success({ confirm }) {
            confirm && uni.navigateTo({ url: "/pages/client/main/setPayPassword" });
          }
        });
        return Promise.reject(res);
      case 309:
        uni.hideToast();
        uni.showModal({
          title: "温馨提示",
          content: res.msg,
          success({ confirm }) {
            confirm && uni.navigateTo({ url: "/pages/client/main/realName" });
          }
        });
        return Promise.reject(res);
      case 501:
        uni.showModal({
          title: "温馨提示",
          content: res.msg,
          showCancel: false,
          success() {
            // #ifdef APP-PLUS
            plus.runtime.quit();
            // #endif
          }
        });
        break;
      default:
        return Promise.resolve(res);
    };
  } catch (error) {
    uni.showToast({ title: "网络异常，请稍后再试~", icon: "none" });
    if ($config.DEBUG_MODE) {
      if (typeof data === "string") {
        if (data) {
          uni.showModal({
            title: statusCode.toString(),
            content: "服务端发生错误",
            confirmText: "去看看",
            cancelText: "先不管",
            success: ({ cancel }) => {
              if (cancel) return;
              uni.setStorageSync("richText", data);
              uni.navigateTo({
                url: transformURL("/pages/common/webview",
                  { url: encodeURIComponent(transformURL("/document.html")) }
                )
              });
            }
          });

          return Promise.reject({ code: statusCode, msg: "服务端发生错误" });
        }
        uni.showModal({
          title: String(statusCode),
          content: "服务端无响应",
          showCancel: false
        });
        return Promise.reject({ code: statusCode, msg: "服务端无响应" });
      }

      return Promise.reject({ code: statusCode, msg: "服务端无响应", data });
    }

    return Promise.reject(JSON.stringify({ code: statusCode, msg: "服务端异常" }));
  }
}

export const requestInterceptorOptions = (paramsKey: "data" | "formData"): UniApp.InterceptorOptions => ({
  invoke: (args: UniApp.RequestOptions & UniApp.UploadFileOption):
    UniApp.RequestOptions | UniApp.UploadFileOption => requestInvoke(args, paramsKey),
  success: (res: UniApp.RequestSuccessCallbackResult) => requestSuccess(res),
  fail({ errMsg }: UniApp.GeneralCallbackResult): void {
    // if (/(^request:fail abort$)|(^request:fail$)/.test(errMsg)) return;
    $config.DEBUG_MODE && uni.showToast({ title: String(errMsg), icon: "none" });
  },
  complete(res: UniApp.GeneralCallbackResult): void {
    $config.DEBUG_MODE && console.log(`%c Response`, "color: #5b6cf2;", res);
  }
});

export function connectWebsocket() {
  if (!$config.URL_WEBSOCKET) return;
  let [socketOpen, socketMsgQueue]: [boolean, string[]] = [false, []];

  uni.connectSocket({ url: $config.URL_WEBSOCKET });
  uni.onSocketOpen((res) => {
    socketOpen = true;
    for (let i = 0; i < socketMsgQueue.length; i++) {
      socketOpen
        ? uni.sendSocketMessage({ data: socketMsgQueue[i] })
        : socketMsgQueue.push(socketMsgQueue[i]);
    }
    socketMsgQueue = [];
    // uni.closeSocket({});
  });
  uni.onSocketMessage((res) => {
    console.log("Received server content:" + res.data);
  });
  uni.onSocketClose((res) => {
    socketOpen = false;
    console.log("Websocket closed!");
  });
  uni.onSocketError((res) => {
    console.log("Websocket connection opening failed, please check!");
  });
}

export function checkVersion({ showToast } = { showToast: false }) {
  const client = getClient();
  function getLatestVersion(params: { osName: string, appVersionCode: number | string, brand?: string, platform: string }) {
    uni.request({
      url: "@noEncrypt" + $config.API_CHECK_VERSION,
      method: "POST",
      data: {
        ...params,
        type: osName === "ios" ? 1 : 2
      },
      success: <any>(({ data, msg: title }: Response) => {
        switch (client) {
          case 5:
          case 6:
            if (+params.appVersionCode < +data.release_version) {
              if (!data.url) {
                showToast && uni.showToast({ title: "缺少下载地址", icon: "none" });
                return;
              }
              uni.setStorage({
                key: "upgradeInfo",
                data: {
                  title: "更新提醒",
                  forceUpdate: true,
                  description: "当前版本过低，需下载新版本安装后使用",
                  upgradeURL: data.url,
                },
                success() {
                  uni.navigateTo({ url: $config.PAGE_UPGRADE });
                }
              });
              return;
            }

            if (+params.appVersionCode < +data.num) {
              if (!data.file_url) {
                showToast && uni.showToast({ title: "缺少更新地址", icon: "none" });
                return;
              }
              uni.setStorage({
                key: "upgradeInfo",
                data: {
                  title: data.title || "",
                  forceUpdate: !!data.m_edit,
                  description: data.content || "",
                  upgradeURL: data.file_url,
                },
                success() {
                  uni.navigateTo({ url: $config.PAGE_UPGRADE });
                }
              });
            }
            showToast && uni.showToast({ title: "已是最新版本！", icon: "none" });
            break;
          case 1:
          case 2:
          case 3:
          case 4:
            if (!data.url) {
              uni.showToast({ title: "无效下载地址", icon: "none" });
              return;
            }
            location.href = data.url;
            break;
          default:
            uni.showToast({ title: "出错了", icon: "error" });
            break;
        }
      }),
      fail({ errMsg: title }) {
        uni.showToast({ title, icon: "error" });
      }
    });
  }

  switch (client) {
    case 5:
    case 6:
      console.log(`${appName} Version ${appWgtVersion || appVersion}`);
      if (!$config.API_CHECK_VERSION) return;
      plus.runtime.getProperty(plus.runtime.appid!, ({ versionCode }) =>
        getLatestVersion({ osName, brand: deviceBrand, appVersionCode: versionCode!, platform: uniPlatform }));
      break;
    case 7:
    case 8:
      const { onCheckForUpdate, onUpdateReady, onUpdateFailed, applyUpdate } = uni.getUpdateManager();

      onCheckForUpdate(({ hasUpdate }) => {
        showToast && uni.showToast({ title: hasUpdate ? "发现新版本，正在更新..." : "已是最新版本！", icon: "none" });

        hasUpdate && console.log("New version found !");
      });

      onUpdateReady((res) => {
        uni.showModal({
          title: "Update Tips",
          confirmText: "Restart",
          cancelText: "Cancel",
          content:
            "The new version is ready. Do you want to restart the application?",
          success: ({ confirm }) => {
            uni.clearStorage();
            confirm && applyUpdate();
          },
        });
      });

      onUpdateFailed((res) => {
        uni.showToast({
          title: "Failed to download the new version. Please try again later.",
          icon: "none",
        });
      });
      break;
    default:
      console.log(
        `%c ${appName} %c Version ${appWgtVersion || appVersion} %c`,
        "background: #35495e; padding: 1px; border-radius: 3px 0 0 3px; color: #fff",
        "background: #007aff; padding: 1px; margin: 1px; border-radius: 0 3px 3px 0; color: #fff; font-weight: bold;",
        "background: transparent"
      );
      getLatestVersion({ osName, appVersionCode, platform: uniPlatform });
      break;
  }
}

export async function setConfig() {
  const value = {};
  if ($config.API_CONFIG_GET) {
    const { data } = await <unknown>uni.request({ url: $config.API_CONFIG_GET }) as Response;
    data && Object.assign(value, data);
  };
  const { data } = await <unknown>uni.request({ url: `${$config.URL_ASSETS}${$config.ASSETS_PREFIX}/static/config.json` }) as Response;
  data && Object.assign(value, data);

  uni.setStorageSync("$config", value);
}

export const beforeRouterInvoke = (url: string) => {
  const pages = getCurrentPages(),
    { route } = pages[pages.length - 1] || { route: "" };

  if (`/${route}` === $config.PAGE_LOGIN_ACCOUNT && `/${route}` === url) {
    return false;
  }

  const excludeRoutes = [
    "pages/client/index",
    "pages/common/*",
    "pages/client/auth/*",
  ];

  const isExclude = excludeRoutes.some(item => new RegExp(item).test(url));

  if (!$config.DEBUG_MODE && !isExclude && ![5, 6].includes(getClient())) {
    uni.showModal({
      title: "温馨提示",
      content: "请在APP内登录使用",
      showCancel: false,
      success({ confirm }) {
        confirm && uni.reLaunch({ url: "/pages/common/download" });
      },
    });
    return false;
  }

  if (!uni.getStorageSync($config.STORAGE_KEY_USER_INFO)) {
    if (isExclude) return true;
    uni.reLaunch({ url: $config.PAGE_LOGIN_ACCOUNT });
    return false;
  }

  return true;
}